#include "QutCase.h"

#include <QTime>

QStringList QutCase::smRelationString_qsl = QStringList()
        << "??" << "" << "==" << "!=" << "<" << "<="
        << ">" << ">=" << "true" << "false" << "??";

QutCase::QutCase(void) {}
QutCase::QutCase(const bool) {}

QutCase::QutCase(const Level level,
                     const Relation relation,
                     const QString & expectedName,
                     const QVariant & expectedValue,
                     const QString & actualName,
                     const QVariant & acutalValue,
                     const int sourceLine,
                     const QString & message)
{
    DEFINE_PROPERTIES_CTORS(TESTCASE_PROPS)
    setTime(QTime::currentTime());
    setLevel(level);
    setRelation(relation);
    setExpectedName(expectedName);
    setExpectedValue(expectedValue);
    setActualName(actualName);
    setActualValue(acutalValue);
    setSourceLine(sourceLine);
    setMessage(message);
}

QutCase::Level QutCase::evaluate(void) const
{
    Level level = Level(getLevel());
    switch (level)
    {
    case Expect:
    case Assert:
    case Quit:
    case Shutdown:
    {
        QVariant expected = getExpectedValue();
        QVariant actual = getActualValue();
        if ( ! actual.convert(expected.type()))
        {
            level = Mismatch;
            break;
        }
        if (expected == actual) level = Pass;
        break;
    }
    default:
        break;
    }
    return level; // stupid compiler tricks
}

QString QutCase::toString(const int flags) const
{
    (void)flags;
    Level level = Level(getLevel());
    Relation rel = Relation(getRelation());
    QString timeBody = getTime().toString("uhh:mm:ss.zzz ");
    QString messageBody = getMessage();
    if ( ! messageBody.isEmpty()) messageBody = " +{" + messageBody +"}";

    return timeBody
            + prefixString(level)
            + bodyString(level, rel)
            + messageBody;
}

QString QutCase::prefixString(const Level level) const
{
    QString rtn("   ");
    switch (level)
    {
    case TestRun:
    case Library:   rtn = QString("===");   break;
    case Enter:     rtn = QString("  >");   break;
    case Leave:     rtn = QString("  <");   break;
    case Expect:
    case Assert:
    case Quit:
    case Shutdown:  rtn = QString("***");   break;
    case Pass:      rtn = QString("   ");   break;
    default:        /* do nothing */        break;
    }
    return rtn;
}

QString QutCase::bodyString(const Level level,
                             const Relation rel) const
{
    QString rtn;
    switch (level)
    {
    case Expect:
    case Assert:
    case Quit:
    case Shutdown:
    case Pass:      rtn = compareBody(level, rel);                  break;
    case TestRun:   rtn = "TestRun started";                        break;
    case Library:   rtn = QString("Library %1 started")
                            .arg(getActualValue().toString());      break;
    case Progress:  rtn = getActualValue().toString();              break;
    case Mismatch:  rtn = QString("%1 (a %2) can't convert to %3 (a %4)")
                            .arg(getActualName())
                            .arg(getActualValue().typeName())
                            .arg(getExpectedName())
                            .arg(getExpectedValue().typeName());    break;
    case Leave:     rtn = QString("%1 State=%2")
                            .arg(getExpectedValue().toString())
                            .arg(getActualValue().toString());      break;
    case Enter:     rtn = QString("%1 at line %2")
                            .arg(getExpectedValue().toString())
                            .arg(getActualValue().toString());      break;
    case Info:      rtn = "Info";                                   break;
    case ToDo:      rtn = "ToDo";                                   break;
    case MustDo:    rtn = "MustDo";                                 break;
    case Summary:
    default:        rtn = "???";                                    break;
    }
    return rtn;
}

QString QutCase::levelSuccess(const Level level) const
{
    QString rtn;
    switch (level)
    {
    case Expect:    rtn = "Fail";           break;
    case Assert:    rtn = "*Assert*";       break;
    case Quit:      rtn = "**Quit**";       break;
    case Shutdown:  rtn = "***ShutDown***"; break;
    case Pass:      rtn = "Pass";           break;
    default:        rtn = "???";            break;
    }
    return rtn;
}

QString QutCase::compareBody(const Level level,
                              const Relation rel) const
{
    QString rtn;
    switch (rel)
    {
    case True:
    case False:
        rtn = QString("%1: %2 is%3 %4")
                    .arg(levelSuccess(level))
                    .arg(getActualName())
                    .arg(Pass == level ? " " : " NOT")
                    .arg(True == rel ? "true" : "false");
        break;

    case LessThan:
    case LessEqual:
    case Equal:
    case NotEqual:
    case MoreEqual:
    case MoreThan:
        rtn = QString("%1: %2 [%3] is%4 %5 %6 [%7]")
                .arg(levelSuccess(level))
                .arg(getExpectedName())
                .arg(getExpectedValue().toString())
                .arg(Pass == level ? " " : " NOT")
                .arg(relationString(rel))
                .arg(getActualName())
                .arg(getActualValue().toString());
        break;

    default:
        break;
    }
    return rtn;
}

QString QutCase::relationString(const Relation rel) const
{
    QString rtn = smRelationString_qsl.at(rel & ~Not);
    if (rel & Not)  rtn.prepend("!");
    return rtn;
}

void QutCase::debug(void) const
{
    qDebug(qPrintable(QString("%1 = %2").arg("Time", 16)
                              .arg(getTime().toString())));
    qDebug(qPrintable(QString("%1 = %2").arg("Level", 16)
                              .arg(getLevel())));
    qDebug(qPrintable(QString("%1 = %2").arg("Relation", 16)
                              .arg(getRelation())));
    qDebug(qPrintable(QString("%1 = %2").arg("ExpectedName", 16)
                              .arg(getExpectedName())));
    qDebug(qPrintable(QString("%1 = %2").arg("ExpectedValue", 16)
                              .arg(getExpectedValue().toString())));
    qDebug(qPrintable(QString("%1 = %2").arg("ActualName", 16)
                              .arg(getActualName())));
    qDebug(qPrintable(QString("%1 = %2").arg("ActualValue", 16)
                              .arg(getActualValue().toString())));
    qDebug(qPrintable(QString("%1 = %2").arg("Message", 16)
                              .arg(getMessage())));
    qDebug(qPrintable(QString("%1 = %2").arg("SourceLine", 16)
                              .arg(getSourceLine())));
    qDebug(qPrintable(QString("%1 = %2").arg("FunctionInfo", 16)
                              .arg(getFunctionInfo())));
    qDebug(qPrintable(QString("%1 = %2").arg("FunctionFile", 16)
                              .arg(getFunctionFile())));
    qDebug(qPrintable(QString("%1 = %2").arg("FunctionLine", 16)
                              .arg(getFunctionLine())));
}
